<!DOCTYPE html>
<html>
<head>
    <title>Animate a cube</title>
    <script type="text/javascript" src="assets/javascripts/three.js"></script>
    <script type="text/javascript" src="assets/javascripts/dat.gui.min.js"></script>
    <script type="text/javascript" src="assets/javascripts/stats.min.js"></script>
    <script type="text/javascript" src="assets/javascripts/tween.js"></script>
    <script type="text/javascript"  src="assets/javascripts/commons.js"></script>
    <script type="text/javascript" src="assets/javascripts/OrbitControls.js"></script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
            font-family: consolas;
        }
    </style>
</head>
<script>

    // global variables
    var renderer;
    var scene;
    var camera;
    var control;
    var stats;
    var cameraControl;

    var isTweening = false;

    function createCube() {

        var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000, transparent: true, opacity: 0.6});
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.castShadow = true;
        cube.name = 'cube';
        cube.position = new THREE.Vector3(2, 2, 0);
        scene.add(cube);
        return cube;
    }
    /**
     * Initializes the scene, camera and objects. Called when the window is
     * loaded by using window.onload (see below)
     */
    function init() {

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render, sets the background color and the size
        renderer = new THREE.WebGLRenderer({antialias: true});
        renderer.setClearColor(0x000000, 1.0);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        // create the ground plane
        var planeGeometry = new THREE.PlaneGeometry(120, 120, 20, 20);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xcccccc});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // rotate and position the plane
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = 0;
        plane.position.z = 0;

        // add the plane to the scene
        scene.add(plane);
        var cube = createCube();

        // add coordinate axies 
        drawCoordinateAxies(scene);


        // position and point the camera to the center of the scene
        camera.position.x = 10;
        camera.position.y = 16;
        camera.position.z = 15;
        camera.lookAt(scene.position);

        cameraControl = new THREE.OrbitControls(camera);

        // add spotlight for the shadows
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(20, 40, 20);
        spotLight.shadowCameraNear = 20;
        spotLight.shadowCameraFar = 150;
        spotLight.castShadow = true;

        scene.add(spotLight);

        // setup the control object for the control gui
        control = new function () {
            this.rotationSpeed = 0.005;
            this.opacity = 0.6;
            this.color = cube.material.color.getHex();

            this.forward = function () {
                takeStepForward(scene.getObjectByName('cube'), 0, 0.5 * Math.PI, 2000);
            };
            this.back = function () {
                takeStepBackward(scene.getObjectByName('cube'), 0, 0.5 * Math.PI, 2000);
            };
            this.left = function () {
                takeStepLeft(scene.getObjectByName('cube'), 0, 0.5 * Math.PI, 2000);
            };
            this.right = function () {
                takeStepRight(scene.getObjectByName('cube'), 0, 0.5 * Math.PI, 2000);
            };
        };

        // add extras
        addControlGui(control);
        addStatsObject();


        // add the output of the renderer to the html element
        document.body.appendChild(renderer.domElement);

        // call the render function, after the first render, interval is determined
        // by requestAnimationFrame
        render();
    }

    function takeStepRight(cube, start, end, time) {
        var cubeGeometry = cube.geometry;
        var widht = 4;
        if (!isTweening) {
            var tween = new TWEEN.Tween({ x: start, cube: cube, previous: 0})
                    .to({ x: end }, time )
                .easing(TWEEN.Easing.Linear.None)
                .onStart(function () {
                    cube.position.y += -widht / 2;
                    cube.position.z += -widht / 2;
                    cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, widht / 2, widht / 2));
                })
                .onUpdate(function () {
                    cube.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(-(this.x - this.previous)));
                    cube.geometry.verticesNeedUpdate = true;
                    cube.geometry.normalsNeedUpdate = true;
                    this.previous = this.x;
                })
                .onComplete(function () {
                    cube.position.y += 2;
                    cube.position.z += -2;
                    cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, -widht / 2, widht / 2));
                    cube.position.x = Math.round(cube.position.x);
                    cube.position.y = Math.round(cube.position.y);
                    cube.position.z = Math.round(cube.position.z);
                    isTweening = false;
                })
                .start();
        }
    }

    function takeStepLeft(cube, start, end, time) {
        var cubeGeometry = cube.geometry;
        var widht = 4;
        if (!isTweening) {
            var tween = new TWEEN.Tween({ x: start, cube: cube, previous: 0})
                    .to({ x: end }, time )
                    .easing(TWEEN.Easing.Linear.None)
                    .onStart(function () {
                        isTweening = true;
                        cube.position.y += -widht / 2;
                        cube.position.z += widht / 2;
                        cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, widht / 2, -widht / 2));
                    })
                    .onUpdate(function () {
                        cube.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(this.x - this.previous));
                        cube.geometry.verticesNeedUpdate = true;
                        cube.geometry.normalsNeedUpdate = true;
                        this.previous = this.x;
                    })
                    .onComplete(function () {
                        cube.position.y += 2;
                        cube.position.z += 2;
                        cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, -widht / 2, -widht / 2));
                        cube.position.x = Math.round(cube.position.x);
                        cube.position.y = Math.round(cube.position.y);
                        cube.position.z = Math.round(cube.position.z);
                        isTweening = false;
                    })
                    .start();
        }
    }

    function takeStepBackward(cube, start, end, time) {
        var widht = 4;
        var cubeGeometry = cube.geometry;

        if (!isTweening) {
            var tween = new TWEEN.Tween( { x: start, cube: cube, previous: 0} )
                    .to( { x: end }, time )
                    .easing( TWEEN.Easing.Linear.None )
                    .onStart( function() {

                        isTweening = true;
                        cube.position.y+=-widht/2;
                        cube.position.x+=widht/2;
                        cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation( -widht/2,  widht/2, 0 ) );
                    })
                    .onUpdate( function () {
                        cube.geometry.applyMatrix( new THREE.Matrix4().makeRotationZ( -(this.x-this.previous) ) );
                        cube.geometry.verticesNeedUpdate=true;
                        cube.geometry.normalsNeedUpdate = true;
                        cube.previous = this.x;
                        this.previous = this.x;
                    } )
                    .onComplete(function() {
                        cube.position.y+=2;
                        cube.position.x+=2;

                        cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation( -widht/2, -widht/2, 0 ) );

                        cube.position.x=Math.round(cube.position.x);
                        cube.position.y=Math.round(cube.position.y);
                        cube.position.z=Math.round(cube.position.z);

                        isTweening = false;
                    })
                    .start();
        }
    }

    function takeStepForward(cube, start, end, time) {
        var widht = 4;
        var cubeGeometry = cube.geometry;

        if (!isTweening) {
            var tween = new TWEEN.Tween( { x: start, cube: cube, previous: 0} )
                    .to( { x: end }, time )
                    .easing( TWEEN.Easing.Linear.None )
                    .onStart( function() {
                        isTweening = true;
                        cube.position.y+=-widht/2;
                        cube.position.x+=-widht/2;
                        cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation( widht/2,  widht/2, 0 ) );
                    })
                    .onUpdate( function () {
                        cube.geometry.applyMatrix( new THREE.Matrix4().makeRotationZ( (this.x-this.previous) ) );

                        cube.geometry.verticesNeedUpdate=true;
                        cube.geometry.normalsNeedUpdate = true;

                        cube.previous = this.x;
                        this.previous = this.x;
                    } )
                    .onComplete(function() {
                        cube.position.y+=widht/2;
                        cube.position.x+=-widht/2;
                        cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation( widht/2, -widht/2, 0 ) );

                        cube.position.x=Math.round(cube.position.x);
                        cube.position.y=Math.round(cube.position.y);
                        cube.position.z=Math.round(cube.position.z);

                        isTweening = false;
                    })
                    .start();
        }
    }


    function addControlGui(controlObject) {
        var gui = new dat.GUI();
        gui.add(controlObject,'forward');
        gui.add(controlObject,'back');
        gui.add(controlObject,'left');
        gui.add(controlObject,'right');

    }

    function addStatsObject() {
        stats = new Stats();
        stats.setMode(0);

        stats.domElement.style.position = 'absolute';
        stats.domElement.style.left = '0px';
        stats.domElement.style.top = '0px';

        document.body.appendChild( stats.domElement );
    }



    /**
     * Called when the scene needs to be rendered. Delegates to requestAnimationFrame
     * for future renders
     */
    function render() {

        // update stats
        stats.update();

        TWEEN.update();

        cameraControl.update();
        // and render the scene
        renderer.render(scene, camera);

        // render using requestAnimationFrame
        requestAnimationFrame(render);
    }


    /**
     * Function handles the resize event. This make sure the camera and the renderer
     * are updated at the correct moment.
     */
    function handleResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    // calls the init function when the window is done loading.
    window.onload = init;
    // calls the handleResize function when the window is resized
    window.addEventListener('resize', handleResize, false);

</script>
<body>
</body>
</html>